#ifndef CUSTOMMOUSEWIDGET_HPP
#define CUSTOMMOUSEWIDGET_HPP

#include <QChartView>
#include <QValueAxis>
#include <QWheelEvent>
#include <QMouseEvent>
#include <QChart>
#include <QAbstractSeries>
#include <QLineSeries>
#include <QObject>
#include <QtMath> // for qFuzzyCompare, qBound, qAbs, etc.
#include <QTimer>
#include <cmath> // for std::round, std::pow
#include <QGraphicsItem> // 需要包含以使用 QGraphicsItem::CacheMode

QT_CHARTS_USE_NAMESPACE

/**
 * @brief 自定义的 QChartView，用于处理鼠标滚轮缩放和左键拖拽平移。
 *
 * 特性：
 * 1. 以鼠标所在的 X 坐标为缩放锚点。
 * 2. 提供最小/最大范围(minXAxisRange ~ maxXAxisRange)，防止过度缩放或拖拽越界。
 * 3. 提供开关以启用或禁用鼠标操作。
 * 4. 使用固定刻度间隔 (TicksFixed + tickInterval) 来减少坐标轴在不同区间的自动布局跳动。
 * 5. 处理多条系列的情况，可通过替换 mapToValue 使用特定系列。
 * 6. 引入节流机制以优化滚动时的性能，避免频繁的重渲染。
 */
class CustomMouseWidget : public QChartView {
    Q_OBJECT
public:
    /**
     * @brief 构造函数：默认禁用鼠标操作。
     * @param chart 指向的 QChart 对象。
     * @param parent 父窗口。
     */
    explicit CustomMouseWidget(QChart *chart, QWidget *parent = nullptr)
        : QChartView(chart, parent)
        , isLeftPressed(false)
        , mouseOperationEnabled(false)
        , maxXAxisRange(10.0)   // 默认最大范围
        , minXAxisRange(0.1)    // 默认最小范围
        , pendingRangeUpdate(false) {
        setMouseTracking(true);
        // 关闭默认的橡皮筋框选功能
        setRubberBand(QChartView::NoRubberBand);

        // 启用图表缓存以提高性能
        chart->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
        // 关闭图表动画以减少缩放时的抖动
        chart->setAnimationOptions(QChart::NoAnimation);

        // ---- 初始化默认的 X 轴刻度设置 (减少抖动的关键) ----
        auto axes = chart->axes(Qt::Horizontal);
        if (!axes.isEmpty()) {
            QValueAxis* axisX = qobject_cast<QValueAxis*>(axes.first());
            if (axisX) {
                axisX->setTickType(QValueAxis::TicksFixed);
                axisX->setTickAnchor(0.0);
                // 设置固定刻度间隔为0.1，可以根据需要调整
                axisX->setTickInterval(0.1);
            }
        }

        // 初始化节流定时器
        throttleTimer.setInterval(16); // 大约60FPS
        throttleTimer.setSingleShot(true);
        connect(&throttleTimer, &QTimer::timeout, this, &CustomMouseWidget::applyPendingRange);
    }

    /**
     * @brief 启用或禁用鼠标操作（拖拽、缩放）。
     * @param enable 如果为 true，启用鼠标操作；否则禁用。
     */
    void setMouseOperationEnabled(bool enable) {
        mouseOperationEnabled = enable;
    }

    /**
     * @brief 设置 X 轴的最大范围。
     * @param maxRange 最大 X 轴范围值。
     */
    void setMaxXAxisRange(qreal maxRange) {
        maxXAxisRange = maxRange;
    }

    /**
     * @brief 设置 X 轴的最小可视范围。
     * @param minRange 最小 X 轴范围值。
     */
    void setMinXAxisRange(qreal minRange) {
        // 避免设为 0 或负数，以防出现除 0 或其他问题
        minXAxisRange = qMax(minRange, 0.000001);
    }

signals:
    /**
     * @brief 当 X 轴范围变化时发出的信号。
     * @param min 新的最小值。
     * @param max 新的最大值。
     */
    void xAxisRangeChanged(qreal min, qreal max);

protected:
    /**
     * @brief 处理鼠标按下事件，记录拖拽状态和位置。
     * @param event 鼠标事件。
     */
    void mousePressEvent(QMouseEvent *event) override {
        if (mouseOperationEnabled && event->button() == Qt::LeftButton) {
            isLeftPressed = true;
            lastMousePos  = event->pos();
        }
        QChartView::mousePressEvent(event);
    }

    /**
     * @brief 处理鼠标移动事件，实现拖拽平移。
     * @param event 鼠标事件。
     */
    void mouseMoveEvent(QMouseEvent *event) override {
        if (mouseOperationEnabled && isLeftPressed) {
            auto axes = chart()->axes(Qt::Horizontal);
            if (!axes.isEmpty()) {
                QValueAxis *axisX = qobject_cast<QValueAxis*>(axes.first());
                if (axisX) {
                    const int dx = event->pos().x() - lastMousePos.x();
                    lastMousePos = event->pos();

                    const qreal oldMin = axisX->min();
                    const qreal oldMax = axisX->max();
                    const qreal range  = oldMax - oldMin;

                    const int viewWidth = this->width();
                    if (viewWidth > 0) {
                        const qreal perPixel = range / viewWidth;
                        const qreal offset   = dx * perPixel;

                        // 计算新的轴范围
                        qreal newMin = oldMin - offset;
                        qreal newMax = oldMax - offset;

                        // -- 保证 newMin >= 0
                        if (newMin < 0) {
                            qreal diff = -newMin;
                            newMin = 0;
                            newMax += diff;
                        }

                        // -- 保证 newMax <= maxXAxisRange
                        if (newMax > maxXAxisRange) {
                            qreal excess = newMax - maxXAxisRange;
                            newMax = maxXAxisRange;
                            newMin -= excess;
                            if (newMin < 0) {
                                newMin = 0;
                            }
                        }

                        // -- 用 EPSILON 忽略极小抖动
                        static constexpr qreal EPSILON = 1e-9;
                        if (qAbs(newMin - oldMin) < EPSILON &&
                                qAbs(newMax - oldMax) < EPSILON) {
                            // 若变动极小则跳过
                            return;
                        }

                        // 如果范围确有变化，则更新
                        if (!qFuzzyCompare(newMin, oldMin) ||
                                !qFuzzyCompare(newMax, oldMax)) {
                            queueRangeUpdate(newMin, newMax);
                        }
                    }
                }
            }
        }
        QChartView::mouseMoveEvent(event);
    }

    /**
     * @brief 处理鼠标释放事件，结束拖拽状态。
     * @param event 鼠标事件。
     */
    void mouseReleaseEvent(QMouseEvent *event) override {
        if (mouseOperationEnabled && event->button() == Qt::LeftButton) {
            isLeftPressed = false;
        }
        QChartView::mouseReleaseEvent(event);
    }

    /**
     * @brief 处理滚轮事件，实现以鼠标所在位置为锚点的缩放。
     * @param event 滚轮事件。
     */
    void wheelEvent(QWheelEvent *event) override {
        if (mouseOperationEnabled) {
            const int delta = event->angleDelta().y();
            if (delta != 0) {
                // 上滚放大 / 下滚缩小
                const qreal factor = (delta > 0) ? 0.8 : 1.25;
                zoomXAxis(factor, event->pos());
                event->accept();
                return;
            }
        }
        QChartView::wheelEvent(event);
    }

private slots:
    /**
     * @brief 应用挂起的范围更新（节流机制）。
     */
    void applyPendingRange() {
        if (pendingRangeUpdate) {
            auto axes = chart()->axes(Qt::Horizontal);
            if (!axes.isEmpty()) {
                QValueAxis *axisX = qobject_cast<QValueAxis*>(axes.first());
                if (axisX) {
                    axisX->setRange(pendingMin, pendingMax);
                    emit xAxisRangeChanged(pendingMin, pendingMax);
                }
            }
            pendingRangeUpdate = false;
        }
    }

private:
    /**
     * @brief 队列范围更新，使用节流机制避免频繁调用 setRange。
     * @param newMin 新的最小值。
     * @param newMax 新的最大值。
     */
    void queueRangeUpdate(qreal newMin, qreal newMax) {
        pendingMin = newMin;
        pendingMax = newMax;
        pendingRangeUpdate = true;
        if (!throttleTimer.isActive()) {
            throttleTimer.start();
        }
    }

    /**
     * @brief 以给定的鼠标位置为锚点，对 X 轴进行缩放。
     * @param factor 缩放因子，小于1放大，大于1缩小。
     * @param mousePos 鼠标在视图中的位置（像素坐标）。
     */
    void zoomXAxis(qreal factor, const QPoint &mousePos) {
        if (chart()->series().isEmpty()) {
            return; // 没有任何 series 时无法映射鼠标位置
        }

        // -- 1) 使用第一个 series 映射鼠标像素 -> 数据坐标
        QAbstractSeries *firstSeries = chart()->series().first();
        QPointF mouseVal = chart()->mapToValue(mousePos, firstSeries);
        qreal anchorX = mouseVal.x();

        // -- 2) 获取 X 轴
        auto axes = chart()->axes(Qt::Horizontal);
        if (axes.isEmpty()) return;
        QValueAxis *axisX = qobject_cast<QValueAxis*>(axes.first());
        if (!axisX) return;

        qreal oldMin = axisX->min();
        qreal oldMax = axisX->max();
        qreal oldRange = oldMax - oldMin;

        // -- 3) 将 anchorX 夹紧 [oldMin, oldMax]
        anchorX = qBound(oldMin, anchorX, oldMax);

        // -- 4) 计算新的范围
        //    factor < 1 => 放大 => 范围变小
        //    factor > 1 => 缩小 => 范围变大
        qreal halfRange = (oldRange * factor) * 0.5;
        qreal newMin = anchorX - halfRange;
        qreal newMax = anchorX + halfRange;
        qreal newRange = newMax - newMin;

        // -- 5) 若比 minXAxisRange 还小，就固定到 minXAxisRange
        if (newRange < minXAxisRange) {
            qreal center = (newMin + newMax) * 0.5;
            newMin = center - minXAxisRange * 0.5;
            newMax = center + minXAxisRange * 0.5;
            newRange = minXAxisRange;
        }

        // -- 6) 夹紧到 [0, maxXAxisRange]
        if (newMin < 0) {
            qreal diff = -newMin;
            newMin = 0;
            newMax += diff;
        }
        if (newMax > maxXAxisRange) {
            qreal excess = newMax - maxXAxisRange;
            newMax = maxXAxisRange;
            newMin -= excess;
            if (newMin < 0) {
                newMin = 0;
            }
        }

        // -- 7) 可选：对小数进行截断或四舍五入，减少浮点数误差
        auto roundToDecimal = [](qreal value, int decimals) -> qreal {
            qreal scale = std::pow(10.0, decimals);
            return std::round(value * scale) / scale;
        };
        newMin = roundToDecimal(newMin, 4);
        newMax = roundToDecimal(newMax, 4);

        // -- 8) 用 EPSILON 忽略极小变动
        static constexpr qreal EPSILON = 1e-8;
        if (qAbs((newMax - newMin) - oldRange) < EPSILON) {
            // 范围几乎没变 => 跳过
            return;
        }

        // -- 9) 如果范围确实变化，就更新
        if (!qFuzzyCompare(newMin, oldMin) || !qFuzzyCompare(newMax, oldMax)) {
            queueRangeUpdate(newMin, newMax);
        }
    }

    bool   isLeftPressed;          ///< 是否按住左键
    bool   mouseOperationEnabled;  ///< 是否启用鼠标操作
    QPoint lastMousePos;           ///< 上一次鼠标位置

    qreal  maxXAxisRange;  ///< X 轴可视的最大范围
    qreal  minXAxisRange;  ///< X 轴最小可视范围（防止缩放过度）

    // 节流机制相关
    QTimer throttleTimer;          ///< 节流定时器
    bool    pendingRangeUpdate;    ///< 是否有待更新的范围
    qreal   pendingMin;            ///< 待更新的最小值
    qreal   pendingMax;            ///< 待更新的最大值
};

#endif // CUSTOMMOUSEWIDGET_HPP
